const remote = require("electron").remote;
const BrowserWindow = require("electron").remote.BrowserWindow;
const ipcRenderer = require("electron").ipcRenderer;
const mainProcess = remote.process;
const path = require("path");
var fs = require("fs");
var tar = require("tar");
const { execFile, exec } = require("child_process");
var Client = require("ssh2-sftp-client");

const Core = require(path.join(__dirname, "./src/core"));
const Common = require(path.join(__dirname, "./src/common"));
const util = require(path.join(__dirname, "./src/util"));
const config = require(path.join(__dirname, "./config"));
const shell = require("electron").shell;
const dialog = require("electron").dialog;
//------------------------------定义基础吧变量---------------------------------
let isDocker = false;
let gameStatus = 0;
const DEPLOY_COMMAND = {
  PACK_BACK: "PACK_BACK",
  DEPLOY_BACK: "DEPLOY_BACK",
  EXTRACT_BACK: "EXTRACT_BACK",
  EXTRACT_FORE: "EXTRACT_FORE",
  PACK_FORE: "PACK_FORE",
  DEPLOY_FORE: "DEPLOY_FORE"
};
const DEPLOY_WORDS = {
  PACK_BACK: "打包后台程序中",
  DEPLOY_BACK: "部署后台文件中",
  EXTRACT_BACK: "解压后台文件中",
  EXTRACT_FORE: "清理前台文件中",
  PACK_FORE: "打包前台dst中",
  DEPLOY_FORE: "部署前台文件中"
};
let DEPLOY_TYPE = null;

let storage = Common.getLocalStorage();
let projectIndex = storage["projectIndex"];
let $controlGame = $("#controlGame");
let $stopServer = $("#stopServer");
let $process = $("#process");
let mClient = new Client();
let ftpIsConnect = false;
let $logContent = $("#log");
let $cleanLog = $("#cleanLog");
let $repoList = $("#repoHistory");
let $Game = {};
log = "";
//初始化操作
//打包，上传等操作进程
let taskWin = new BrowserWindow({
  width: 400,
  height: 400,
  show: false
});
let appWindow;

ipcRenderer.on("init", function(event, fromWindowId) {
  appWindow = BrowserWindow.fromId(fromWindowId);
});

ipcRenderer.on("selected-file", function(e, path) {
  console.log(path);
});

function logs(log) {
  logReply(logTextWithDate(log));
}
(function() {
  let taskPath = "file://" + __dirname + "/task.html";
  const windowID = BrowserWindow.getFocusedWindow().id;
  taskWin.loadURL(taskPath);
  taskWin.webContents.on("did-finish-load", function() {
    taskWin.webContents.send("init", windowID);
  });

  //如果是docker模式则在此建立新的连接
  let item = storage["projects"][projectIndex];
  if (item.docker == "true") {
    Core.connect({
      host: item.host,
      port: item.dockerPort,
      username: "root",
      password: item.dockerPassword
    })
      .then(
        () => {
          tips("容器连接成功", "success");
          return Core.getPid();
        },
        () => {
          tips("容器连失败", "error");
        }
      )
      .then(
        pid => {
          $Game.pid = util.getPid(pid, "erlang");
          logReply(logTextWithDate(`游戏进程PID:${$Game.pid}`));
        },
        () => {
          tips("PID获取失败", "error");
        }
      )
      .catch();
  }
})();

function send(cmd, msg, params) {
  taskWin.webContents.send(cmd, msg, params);
}
ipcRenderer.on("task-msg", function(e, cmd, code) {
  switch (cmd) {
    case DEPLOY_COMMAND.PACK_BACK:
      logs("打包完成");
      NProgress.done();
      DEPLOY_TYPE = DEPLOY_COMMAND.DEPLOY_BACK;
      logs("开始部署后台程序");
      NProgress.start();
      break;
    case DEPLOY_COMMAND.DEPLOY_BACK:
      logs("程序部署完成");
      NProgress.done();
      DEPLOY_TYPE = DEPLOY_COMMAND.EXTRACT_BACK;
      NProgress.start();
      logs("后台程序解压中");
      break;
    case DEPLOY_COMMAND.EXTRACT_BACK:
      NProgress.done();
      tips("后台程序更新成功","success")
      Common.postNotification("更新提示", "后台文件已更新至服务器");
      break;
    case "back-fail":
      NProgress.done();
      $("#progressTitle").css("color", "red");
      $("#progressTitle").text("操作失败");
      Common.postNotification("更新提示", "后台文件更新失败囧");
      break;
    case DEPLOY_COMMAND.PACK_FORE:
      logs("dst打包完成");
      NProgress.done();
      DEPLOY_TYPE = DEPLOY_COMMAND.DEPLOY_FORE;
      NProgress.start();
      break;
    case DEPLOY_COMMAND.DEPLOY_FORE:
      logs("dst上传完成");
      NProgress.done();
      DEPLOY_TYPE = DEPLOY_COMMAND.EXTRACT_FORE;
      NProgress.start();
      break;
    case DEPLOY_COMMAND.EXTRACT_FORE:
      NProgress.done();
      tips("前台文件更新成功", "success");
      Common.postNotification("更新提示", "前台文件已更新至服务器");
      break;
    case "fore-fail":
      NProgress.done();
      $("#progressTitle").css("color", "red");
      $("#progressTitle").text("操作失败");
      Common.postNotification("更新提示", "前台文件更新失败囧");
      break;
    case "ftp":
      if (code == 1) {
        tips("已连接FTP", "success");
      } else {
        tips("连接FTP失败", "error");
      }
      break;
    case "ssh":
      if (code == 1) {
        tips("已连接控制台", "success");
      }
      break;
    case "pid":
      if (code) {
        $process.attr("title", "检测到游戏进程");
        $process.addClass("online");
      } else {
        logs("未检测到游戏进程");
        $process.attr("title", "未检测到游戏进程");
        $process.removeClass("online");
      }
      break;
    case "not-run":
      tips("游戏未运行", "warning");
      break;
    case "alreadyrun":
      tips("游戏已经在运行", "warning");
      break;
    case "repo":
      if (code.indexOf("Initialized") != -1) {
        tips("远程仓库初始化完成", "success");
      }
      break;
    case "ssh":
      if (code) {
        tips("ssh更新失败", "error");
      } else {
        tips("ssh更新成功", "success");
      }
      break;
    case "del-fore-repo":
      if (code) {
        tips("删除远端仓库失败", "error");
      } else {
        tips("删除远端仓库成功", "success");
      }
      break;
    case "repo-hook":
      if (code) {
        tips("hook上传失败", "error");
      } else {
        tips("hook上传完成", "success");
      }
      break;
    case "start":
      code == "ok" ? tips("游戏启动成功", "success") : tips("游戏启动失败", "error");
      break;
    case "stop":
      if (code == "ok") {
        tips("游戏已关闭", "success");
        $process.attr("title", "未检测到游戏进程");
        $process.removeClass("online");
      } else {
        tips("游戏关闭失败", "error");
        $process.attr("title", "检测到游戏进程");
        $process.addClass("online");
      }
      break;
    case "del-dst":
      if (code) {
        tips("远程dst删除失败", "error");
      } else {
        tips("远程dst删除成功", "success");
      }
      break;
    case "repo-history":
      showHistory(code);
      break;
    case "init":
      initData();
      break;
    case "roll-back":
      console.log(code);
      if (code.indexOf("is now at") != -1) {
        tips("版本回退成功", "success");
      }
      break;
    case "roll-back-cp":
      console.log(code);
      break;
  }
});

//初始化数据
function initData() {
  send("task", "repo-history");
}

//关闭窗口
$(".js_statusBar_close").on("click", function() {
  taskWin.webContents.send("close");
  remote.BrowserWindow.getFocusedWindow().close();
});
$repoList.on("click", ".js_rollback_item", function() {
  let $this = $(this);
  rollBack($this.attr("title"));
});

//选择文件
function chooseBackPack() {
  ipcRenderer.send("open-file-dialog");
}

//进本信息录入
let inputs = $("#base-info").find("input");
inputs.each(function(i, el) {
  let item = storage["projects"][projectIndex];
  item[el.name] ? (el.value = item[el.name]) : "";
});
inputs.on("blur", function() {
  let val =
    this.name == "localWorkDir" ? this.value.replace(/\\/g, "/") : this.value;
  storage["projects"][projectIndex][this.name] = val;
  Common.setLocalStorage(storage);
  this.name == "localWorkDir" ? (this.value = val) : "";
  send("task", "localstorage");
});
$("#sshModal textarea").val(storage["projects"][projectIndex].ssh);
function saveSSH(el) {
  if (storage["projects"][projectIndex].ssh == el.value) return;
  storage["projects"][projectIndex].ssh = el.value;
  let isInitSsh = storage["projects"][projectIndex].initSsh;
  isInitSsh ? "" : (storage["projects"][projectIndex].initSsh = true);
  Common.setLocalStorage(storage);
  logs("准备更新ssh key")
  send("task", "ssh", {
    initSsh: storage["projects"][projectIndex].initSsh,
    key: el.value
  });
}

//开始 打包-部署-解压 后台
function allUpdataBack() {
  DEPLOY_TYPE = DEPLOY_COMMAND.PACK_BACK;
  NProgress.start();
  send("task", DEPLOY_COMMAND.PACK_BACK);
}
function allUpdataFore() {
  DEPLOY_TYPE = DEPLOY_COMMAND.PACK_FORE;
  NProgress.start();
  send("task", DEPLOY_COMMAND.PACK_FORE);
}
function checkErlangEnv() {
  send("task", "check");
}
function startServer() {
  let item = storage["projects"][projectIndex];
  logs("准备启动游戏");
  if (item.docker == "true") {
    dockerStartServer();
    return;
  }
  send("task", "start");
}
function stopServer() {
  let item = storage["projects"][projectIndex];
  logs("正在停止游戏");
  if (item.docker == "true") {
    dockerStopServer();
    return;
  }
  send("task", "stop");
}
function initRemoteForeRepo() {
  logs("准备初始化远程仓库");
  send("task", "fore-repo");
}
function delLocalRepo() {
  logs("开始删除本地仓库");
  removeLocalRepo();
}
function delRemoteRepo() {
  logs("准备删除远程仓库");
  send("task", "del-fore-repo");
}
//删除htdoc下的dst
function delRemoteDst() {
  logs("准备删除远程dst");
  send("task", "del-dst");
}
function singleFore() {
  logs("开始上传dst中")
  send("task","single-fore")
}
//------------------------------GIT操作--------------------------------
/**
 * 初始化前台dst仓库
 */
function initLocalForeRepo() {
  let item = storage["projects"][projectIndex];
  exec(
    `git init && git remote add origin ssh://${item.username}@${item.host}:${item.port}${config.git_dir}${config.fore_git_repo}.git`,
    {
      cwd: path.resolve(
        item.localWorkDir,
        config.fore_pack_dir,
        config.fore_pack_target_dir
      )
    },
    function(error, stdout, stderr) {
      if (stdout.indexOf("Initialized") != -1) {
        tips("本地仓库初始化完成", "success");
        copy()
          .then(() => {
            new $.zui.Messager("脚本复制完成", {
              icon: "ok",
              type: "success"
            }).show();
          })
          .catch(() => {
            tips("我擦，复制失败?", "error");
          });
      } else if (stdout.indexOf("existing") != -1) {
        tips("本地仓库已存在", "info");
      } else {
        tips("本地仓库初始化失败", "errir");
      }
    }
  );
}
//复制提交脚本到dst目录
function copy() {
  let item = storage["projects"][projectIndex];
  return new Promise((resolve, reject) => {
    exec(
      `copy ${path.join(__dirname, "./scripts/push.bat")} ${path.resolve(
        item.localWorkDir,
        config.fore_pack_dir,
        config.fore_pack_target_dir,
        "push.bat"
      )}`,
      (error, stdout, stderr) => {
        if (error) reject();
        resolve(stdout);
      }
    );
  });
}

/**
 * 执行脚本进行提交
 */
function commit(context) {
  context.disabled = true;
  let oldText = context.innerHTML;
  context.innerText = "更新中";
  let item = storage["projects"][projectIndex];
  let ex = execFile(
    "push.bat",
    {
      maxBuffer: 1024 * 1024,
      cwd: path.resolve(
        item.localWorkDir,
        config.fore_pack_dir,
        config.fore_pack_target_dir
      )
    },
    (error, stdout, stderr) => {
      context.disabled = false;
      context.innerHTML = oldText;
      if (error) {
        console.log(error);
        tips("额。。好像推送失败了?", "error");
        return;
      }
      let msg = stdout.split("\n")[0];
      if (stdout.indexOf("nothing to commit") != -1) {
        tips("无新内容可推送", "info");
      } else {
        tips("推送完成", "success");
      }
    }
  );
}
//删除本地仓库
function removeLocalRepo() {
  let item = storage["projects"][projectIndex];
  exec(
    "rmdir /q /s .git",
    {
      cwd: path.resolve(
        item.localWorkDir,
        config.fore_pack_dir,
        config.fore_pack_target_dir
      )
    },
    (error, stdout, stderr) => {
      if (error) {
        tips("删除出错或文件夹不存在", "error");
      } else {
        tips("删除本地仓库成功", "success");
      }
    }
  );
}
//仓库历史
function showHistory(stdout) {
  let html = "";
  let hisArr = stdout.split("\n");
  for (var index = 0; index < hisArr.length; index++) {
    if (!hisArr[index] || hisArr[index].indexOf("fatal") != -1) continue;
    var element = hisArr[index].split(" ");
    html += `<li><div><p title="commit id">${element[0]}</p><p>${element[2]} ${element[3]}</p></div> <a class="js_rollback_item" href="javascript:;" title="${element[0]}"><i class="icon icon-undo"></i> 回滚此版本</a></li>`;
  }
  $repoList.html(html);
}

function rollBack(commitid) {
  logs("准备回退版本");
  send("task", "roll-back", commitid);
}

//=====================================辅助函数=================================
function tips(msg, type) {
  let opt = {};
  switch (type) {
    case "success":
      opt = { icon: "ok", type: "success" };
      break;
    case "error":
      opt = { icon: "exclamation-sign", type: "danger" };
      break;
    case "info":
      opt = { icon: "info-sign", type: "info" };
      break;
    case "warning":
      opt = { icon: "warning-sign", type: "warning" };
      break;
  }
  new $.zui.Messager(msg, opt).show();
  logs(msg);
}

//=================================docker模式下 启动游戏 关闭游戏
//启动游戏
function dockerStartServer() {
  let project = storage["projects"][projectIndex];
  let cm = `cd ${project.dockerPath}/pi/back/boot/ && sh ${project.startScript ||
    "start.sh"}`;
  new Promise((resolve, reject) => {
    Core.conn.exec(cm, (error, stream) => {
      streamWork(resolve, reject, stream);
    });
  })
    .then(() => {
      tips("游戏启动成功", "success");
      dockerCheck();
    })
    .catch(() => {
      tips("游戏启动失败", "error");
    });
}
//关闭游戏
function dockerStopServer() {
  if ($Game.pid) {
    Core.stopProcess($Game.pid)
      .then(() => {
        tips("游戏已关闭", "success");
        dockerCheck();
      })
      .catch(() => {
        dockerCheck();
        tips("游戏关闭失败", "error");
      });
  } else {
    tips("游戏未运行", "warning");
  }
}

function dockerCheck() {
  Core.getPid().then(pid => {
    let gpid = util.getPid(pid, "erlang");
    console.log(gpid);
    $Game.pid = gpid;
    if (gpid) {
      $process.attr("title", "检测到游戏进程");
      $process.addClass("online");
    } else {
      $process.attr("title", "未检测到游戏进程");
      $process.removeClass("online");
    }
  });
}

//------------------------------进度条函数---------------------------------
let NProgress = {};
let $progress = $("#progress");
let $progressNum = $("#progressNum");
NProgress.trickle = function() {
  return NProgress.inc();
};
NProgress.inc = function(amount) {
  var n = NProgress.status;
  if (!n) {
    return NProgress.start();
  } else if (n >= 1) {
    return;
  } else {
    if (typeof amount !== "number") {
      if (n >= 0 && n < 0.2) {
        amount = 0.1;
      } else if (n >= 0.2 && n < 0.5) {
        amount = 0.04;
      } else if (n >= 0.5 && n < 0.8) {
        amount = 0.02;
      } else if (n >= 0.8 && n < 0.99) {
        amount = 0.005;
      } else {
        amount = 0;
      }
    }

    n = clamp(n + amount, 0, 0.994);
    return NProgress.set(n);
  }
  return this;
};

let clearTimer = null;
NProgress.set = function(n) {
  n = clamp(n, 0.08, 1);
  NProgress.status = n === 1 ? null : n;
  $progress.css("width", `${n * 100}%`);
  $progressNum.text(`${DEPLOY_WORDS[DEPLOY_TYPE]} ${(n * 100).toFixed(2)}%`);
  if (n == 1) {
    clearTimer = setTimeout(function() {
      clearTimer = null;
      $progressNum.parent().fadeOut();
      $("#progressTitle").fadeIn();
      NProgress.status = 0;
      $progress.css("width", "0%");
      return;
    }, 300);
  }
  return this;
};
NProgress.start = function() {
  if (clearTimer) clearTimeout(clearTimer);
  $("#progressTitle").css("display", "none");
  $("#progressTitle").css("color", "#e5e5e5");
  $progressNum.parent().css("display", "block");
  if (!NProgress.status) NProgress.set(0);
  var work = function() {
    setTimeout(function() {
      if (!NProgress.status) return;
      NProgress.trickle();
      work();
    }, 500);
  };
  work();
  return this;
};
NProgress.done = function() {
  NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
};

function clamp(n, min, max) {
  if (n < min) return min;
  if (n > max) return max;
  return n;
}

// 时间格式
function dateFormat(date) {
  if (date.toString().length == 1) {
    return "0" + date;
  }
  return date;
}
/** 日志输出带时间戳*/
function logTextWithDate(content) {
  let D = new Date();
  let h = dateFormat(D.getHours());
  let m = dateFormat(D.getMinutes());
  let s = dateFormat(D.getSeconds());
  return `[${h}:${m}:${s}] ${content}<br/>`;
}

function logReply(data) {
  let originData = data;
  data = data.replace(/\n/g, "<br/>");
  data = data.replace(
    /\[(.*?)\]/g,
    '[<span class="operation_stage_log_time">$1</span>]'
  ); // 时间高亮
  data = data.replace(
    /\'(.*?)\'/g,
    "'<span class=\"operation_stage_log_keyword\">$1</span>'"
  ); // 单引号内的关键词高亮
  data = data.replace(
    /(D .*?):/,
    "'<span class=\"operation_stage_log_qmui\">$1</span>'"
  ); // QMUI 任务名高亮
  $logContent.append(`${data}`);
  $logContent.scrollTop($logContent.get(0).scrollHeight);
  // 把 log 写入 sessionStorage
  log += data;
}
$cleanLog.on("click", function() {
  // 清空 Log 界面
  $logContent.html("");
  // 清空 Storage
  log = "";
});

function streamWork(resolve, reject, stream) {
  let stdout = "";
  stream
    .on("close", function(code, signal) {
      resolve(stdout);
    })
    .on("data", function(data) {
      stdout += data;
    })
    .stderr.on("data", function(data) {
      stdout += data;
    });
}
